#include "log.h"
#include "args.h"
#include "copy.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#define BUFLEN 5
#define INTERVAL 1
#define del() putchar('\b')
#define clean()					\
	for (int i=0; i<BUFLEN-1; i++) del();	\
	del()
	
int __sock_fd = 0;
struct sockaddr_nl* __src_addr = NULL;
struct sockaddr_nl* __dst_addr = NULL;

void clean_resource() {
	reset_log_fd();
	clean_args(&ag);
}

bool prepare_socket()
{
	__sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);	
	if (!__sock_fd) return false;
	
	__src_addr = (struct sockaddr_nl*)malloc(sizeof(struct sockaddr_nl));
	__dst_addr = (struct sockaddr_nl*)malloc(sizeof(struct sockaddr_nl));

	if (__src_addr&&__dst_addr) {
		memset(__src_addr, 0, sizeof(struct sockaddr_nl));
		__src_addr->nl_family = AF_NETLINK;
		__src_addr->nl_pid = getpid();
		__src_addr->nl_groups = 0;
		bind(__sock_fd, (struct sockaddr*)__src_addr, sizeof(struct sockaddr_nl));
		memset(__dst_addr, 0, sizeof(struct sockaddr_nl));
		__dst_addr->nl_family = AF_NETLINK;
		__dst_addr->nl_pid = 0;
		__dst_addr->nl_groups = 0;
		return true;
	} else return false;
}

char get_char(size_t prog)
{
	prog %= 4;
	switch (prog) {
	case 0:
		return '|';
		break;
	case 1:
		return '/';
		break;
	case 2:
		return '-';
		break;
	case 3:
		return '\\';
		break;
	}
}

void fill_buf(char* buf, size_t prog)
{
	char* pos = buf + BUFLEN - 1;
	memset(buf, ' ', BUFLEN);

	*pos = '\0';
	--pos;
	*pos = '%';
	--pos;

	if (!prog) {
		*pos = '0';
		return;
	}

	while (prog) {
		size_t value = prog%10;
		*pos = '0' + value;
		prog /= 10;
		--pos;
	}
}

void print_progress(size_t idx, size_t prog)
{
	char buf[BUFLEN];
	putchar(get_char(idx));
	fill_buf(buf, prog);
	for (int i=0; i<BUFLEN-1; i++) putchar(buf[i]);
	fflush(stdout);
}

int main(int argc, char** argv)
{
#ifdef TEST
	test_code = atoi(argv[1]);
	--argc;
	++argv;
#endif
	atexit(clean_resource);

	if (!prepare_socket()) {
		CAMERA_STDOUT_THEN_LOG("Camera failed to get its driver ready!\n");
		return -1;
	}

	if (!parse_args(argc, argv)) {
		CAMERA_STDOUT_THEN_LOG("Camera failed to parse arguments!\n");
		return -1;
	}

	if (build_file_tree(__sock_fd, __dst_addr) == -1) {
		CAMERA_STDOUT_THEN_LOG("Camera failed to build file tree!\n");
		return -1;
	}
	
	ssize_t seq;
	if ((seq=prepare_mount_point(__sock_fd)) == -1) {
		CAMERA_STDOUT_THEN_LOG("Camera failed to prepare mount point!\n");
		return -1;
	}

	if (start_copy(__sock_fd, __dst_addr, seq) == -1) {
		CAMERA_STDOUT_THEN_LOG("Camera failed to do copy-on-write!\n");
		return -1;
	}

	CAMERA_STDOUT_THEN_LOG("Camera started block-copy process!\n");
	
	size_t idx = 0;
	size_t prog = 0;
	while ((prog=query_progress(__sock_fd, __dst_addr, seq)) <= 100) {
		print_progress(idx++, prog);
		if (prog == 100) {
			CAMERA_STDOUT("\n");
			CAMERA_STDOUT_THEN_LOG("Done!");
			CAMERA_STDOUT("\n");
			break;
		}
		clean();
		sleep(INTERVAL);
	}

	return 0;
}
